// reference to pbrt-v3 bound

#ifndef __BOUND_H__
#define __BOUND_H__
#include <limits>
#include <type_traits>
#include <iterator>
#include "vec2.h"
#include "vec3.h"
#include "vec4.h"

template <class T, size_t Size>
class Bound;

class BoundIter : public std::forward_iterator_tag {
public:
    BoundIter(const Bound<int, 2>* bound, detail::Vec<int, 2>&& point);
    BoundIter(const Bound<int, 2>* bound);

    BoundIter& operator ++ ();
    BoundIter operator ++ (int);

    friend bool operator == (const BoundIter& lhs, const BoundIter& rhs) {
        return (lhs.mBound == rhs.mBound) && (lhs.mCurrPoint == rhs.mCurrPoint);
    }

    friend bool operator != (const BoundIter& lhs, const BoundIter& rhs) {
        return (lhs.mBound != rhs.mBound) || (lhs.mCurrPoint != rhs.mCurrPoint);
    }

    detail::Vec<int, 2>& operator * ();
private:
    void advance();

    detail::Vec<int, 2> mCurrPoint;
    const Bound<int, 2>* mBound;
};

template <class T, size_t Size>
class Bound {
public:
    Bound();
    Bound(const detail::Vec<T, Size>& p1, const detail::Vec<T, Size>& p2);

    BoundIter begin() {
        static_assert(std::is_integral_v<T>);
        static_assert(Size == 2);
        return BoundIter(this);
    }

    BoundIter end() {
        static_assert(std::is_integral_v<T>);
        static_assert(Size == 2);
        detail::Vec<int, 2> endP = pMax;
        endP.y += 1;
        return BoundIter(this, std::move(endP));
    }

    detail::Vec<T, Size> diagonal() const;
    T volume() const;
    size_t maximumExtent() const;
    template <class U>
    auto lerp(const detail::Vec<U, Size>& t) -> detail::Vec<std::common_type_t<T, U>, Size> const;
    detail::Vec<T, Size> offset(const detail::Vec<T, Size>& p) const;
    void boundingSphere(detail::Vec<T, Size>* center, float* radius) const;

    detail::Vec<T, Size> pMin;
    detail::Vec<T, Size> pMax;
};

#include "bound.inl"

#endif